package tianrun.ziguan.api.calculate.config.dto.request.common.list;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import io.swagger.annotations.ApiModel;
import tianrun.ziguan.api.calculate.config.config.exception.CodeException;
import tianrun.ziguan.api.calculate.config.config.exception.CommonErrorCode;
import tianrun.ziguan.api.calculate.config.utils.DateUtil;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@ApiModel
public class FilterField {

    private String fieldName;
    private FilterTypeEnum filterType;
    private Object condition;
    private ConditionalSplicing conditionalSplicing;
    private List<FilterField> children;

    public FilterField(String fieldName, FilterTypeEnum filterType, Object condition, ConditionalSplicing conditionalSplicing, List<FilterField> children) {
        this.fieldName = fieldName;
        this.filterType = filterType;
        this.condition = condition;
        this.conditionalSplicing = conditionalSplicing;
        this.children = children;
    }

    public FilterField() {
    }

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public FilterTypeEnum getFilterType() {
        return filterType;
    }

    public void setFilterType(FilterTypeEnum filterType) {
        this.filterType = filterType;
    }

    public Object getCondition() {
        return condition;
    }

    public void setCondition(Object condition) {
        this.condition = condition;
    }

    public ConditionalSplicing getConditionalSplicing() {
        return conditionalSplicing;
    }

    public void setConditionalSplicing(ConditionalSplicing conditionalSplicing) {
        this.conditionalSplicing = conditionalSplicing;
    }

    public List<FilterField> getChildren() {
        return children;
    }

    public FilterField setChildren(List<FilterField> children) {
        this.children = children;
        return this;
    }

    public static FilterField in(String name, Object o) {
        FilterField filterField = new FilterField();
        filterField.setConditionalSplicing(FilterField.ConditionalSplicing.and);
        filterField.setFieldName(name);
        filterField.setFilterType(FilterField.FilterTypeEnum.in);
        filterField.setCondition(o);
        return filterField;
    }

    public static FilterField equal(String name, Object o) {
        FilterField filterField = new FilterField();
        filterField.setConditionalSplicing(FilterField.ConditionalSplicing.and);
        filterField.setFieldName(name);
        filterField.setFilterType(FilterTypeEnum.equal);
        filterField.setCondition(o);
        return filterField;
    }

    public void appendClause(StringBuilder sb) {
        if (FilterTypeEnum.children.equals(filterType) && children != null && !children.isEmpty()) {
            if (!sb.toString().endsWith(StringPool.LEFT_BRACKET)) {
                sb.append(StringPool.SPACE).append(conditionalSplicing.splicing).append(StringPool.SPACE);
            }
            sb.append(StringPool.LEFT_BRACKET);
            for (FilterField child : children) {
                child.appendClause(sb);
            }
            sb.append(StringPool.RIGHT_BRACKET);
        } else if (FilterTypeEnum.range.equals(filterType) || FilterTypeEnum.rangeTime.equals(filterType)) {
            Object[] objects;
            if (condition instanceof String) {
                objects = condition.toString().split(StringPool.COMMA);
            } else if (condition instanceof Collection) {
                objects = ((Collection<?>) condition).toArray();
            } else {
                throw new CodeException(CommonErrorCode.FILTER_FIELDS_ERROR);
            }
            if (FilterTypeEnum.rangeTime.equals(filterType)) {
                objects = Arrays.stream(objects).map(item -> DateUtil.parseTimeStr(Long.parseLong(item.toString()))).toArray();
            }
            if (!sb.toString().endsWith(StringPool.LEFT_BRACKET)) {
                sb.append(StringPool.SPACE).append(conditionalSplicing.splicing).append(StringPool.SPACE);
            }
            sb.append(fieldName).append(String.format(filterType.symbol, objects)).append(fieldName);
        } else {
            String val = val(filterType);
            if (val == null && !FilterTypeEnum.exist.equals(filterType) && !FilterTypeEnum.notExist.equals(filterType)) {
                throw new CodeException(CommonErrorCode.FILTER_FIELDS_ERROR);
            }
            if (!sb.toString().endsWith(StringPool.LEFT_BRACKET)) {
                sb.append(StringPool.SPACE).append(conditionalSplicing.splicing).append(StringPool.SPACE);
            }
            String str;
            if (FilterTypeEnum.like.equals(filterType)) {
                str = "like concat('%'," + val + ",'%')";
            } else {
                str = val == null ? filterType.symbol : String.format(filterType.symbol, val);
            }
            sb.append(fieldName).append(StringPool.SPACE).append(str);
        }
    }

    public String val(FilterTypeEnum filterType) {
        if (condition instanceof Collection) {
            Collection<?> condition1 = (Collection<?>) condition;
            if (condition1.isEmpty()) {
                return null;
            }
            return condition1.stream().map(this::parse).collect(Collectors.joining("','", StringPool.SINGLE_QUOTE, StringPool.SINGLE_QUOTE));
        } else if (condition instanceof String) {
            String ss = (String) condition;
            if (ss.contains(StringPool.COMMA) && FilterTypeEnum.in.equals(filterType)) {
                return String.format("'%s'", String.join("','", ss.split(StringPool.COMMA)));
            } else {
                return String.format("'%s'", condition);
            }
        } else if (condition instanceof Number) {
            return String.valueOf(condition);
        }
        return null;
    }

    public String parse(Object o) {
        if (o instanceof String) {
            return o.toString();
        } else if (o instanceof Number) {
            return String.valueOf(o);
        } else {
            return StringPool.EMPTY;
        }
    }

    public enum FilterTypeEnum {
        equal("= %s"), likeRight("like concat(%s, '%')"), like("like concat('%', %s,'%')"), in("in(%s)"), gt("> %s"), lt("< %s"), ge(">= %s"), le("<= %s"), ne("<> %s"), exist("is not null"), notExist("is null"), range(">= '%s' AND '%s' >="), rangeTime(range.symbol), children;

        FilterTypeEnum(String symbol) {
            this.symbol = symbol;
        }

        FilterTypeEnum() {
            this.symbol = this.name();
        }

        public final String symbol;
    }

    public enum ConditionalSplicing {
        and("AND"), or("OR");

        ConditionalSplicing(String splicing) {
            this.splicing = splicing;
        }

        public final String splicing;
    }

}
